package cn.broadmax.boot.config;

import cn.broadmax.boot.shiro.HttpMethodOverrideFilter;
import cn.broadmax.boot.shiro.JwtFilter;
import cn.broadmax.boot.shiro.ShiroRealm;
import jexx.util.StringUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro配置
 * @author jeff
 * @date 2020/6/17
 */
@Slf4j
@Data
@Configuration
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfig {

    @Autowired
    private ShiroProperties shiroProperties;

    /**
     * 为spring应用 定义shiro的主filter
     */
    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //url白名单
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        if(shiroProperties.getExcludeUrls() != null){
            String[] permissionUrl = shiroProperties.getExcludeUrls();
            for(String url : permissionUrl){
                filterChainDefinitionMap.put(url,"anon");
            }
        }

        filterChainDefinitionMap.put("/auth/login*", "anon");
        filterChainDefinitionMap.put("/thirdLogin/type/*", "anon");

        //swagger-ui
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/**.js", "anon");
        filterChainDefinitionMap.put("/**.css", "anon");
        filterChainDefinitionMap.put("/**.jpg", "anon");
        filterChainDefinitionMap.put("/**.png", "anon");
        filterChainDefinitionMap.put("/**.svg", "anon");
        filterChainDefinitionMap.put("/**.html", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger**/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/v2/**", "anon");

        //druid
        filterChainDefinitionMap.put("/druid/**", "anon");

        //自定义过滤器
        Map<String, Filter> filterMap = new HashMap<>(2);
        filterMap.put("httpMethodOverrideFilter", new HttpMethodOverrideFilter());
        filterMap.put("jwtFilter", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        filterChainDefinitionMap.put("/**", "httpMethodOverrideFilter,jwtFilter");

        //默认登陆接口
        shiroFilterFactoryBean.setLoginUrl("/sys/login");
        //未授权接口
        shiroFilterFactoryBean.setUnauthorizedUrl("/sys/common/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    ShiroFilterChainDefinition shiroFilterChainDefinition() {
       return new DefaultShiroFilterChainDefinition();
    }

    @Bean
    public ShiroRealm createRealm(){
        return new ShiroRealm();
    }

    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager(AuthorizingRealm realm,
                                                     Authorizer authorizer,
                                                     RedisProperties redisProperties) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthorizer(authorizer);
        securityManager.setCacheManager(redisCacheManager(redisProperties));
        securityManager.setRealm(realm);

        //http://shiro.apache.org/session-management.html
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);

        return securityManager;
    }

    private RedisCacheManager redisCacheManager(RedisProperties redisProperties) {
        log.info("===============(1)创建缓存管理器RedisCacheManager");
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager(redisProperties));
        //redis中针对不同用户缓存(此处的id需要对应user实体中的id字段,用于唯一标识)
        redisCacheManager.setPrincipalIdFieldName("id");
        //用户权限信息缓存时间
        redisCacheManager.setExpire(60);
        return redisCacheManager;
    }

    /**
     * shiro注解支持
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
//        /**
//         * 解决重复代理问题 github#994
//         * 添加前缀判断 不匹配 任何Advisor
//         */
//        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
//        defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public RedisManager redisManager(RedisProperties redisProperties) {
        RedisManager redisManager = new RedisManager();
        String host = StringUtil.format("{}:{}", redisProperties.getHost(), redisProperties.getPort());
        redisManager.setHost(host);
        redisManager.setTimeout(0);
        if(StringUtil.isNotEmpty(redisProperties.getPassword())){
            redisManager.setPassword(redisProperties.getPassword());
        }
        return redisManager;
    }

}
